﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Collections;
using System.Windows.Forms.VisualStyles;
using System.Reflection;
using System.ComponentModel;
using System.Drawing.Drawing2D;
using System.Diagnostics;

namespace Maxel
{
    public class TrackDataGridViewCell : DataGridViewCell
    {
        public TrackDataGridViewCell()
            : base()
		{
			_rect = new List<Rectangle>();
		}

        private bool _dataGridViewEventsSet;
        protected override void OnDataGridViewChanged()
        {
            base.OnDataGridViewChanged();
            if (this.DataGridView != null && !_dataGridViewEventsSet)
            {
                DataGridView.MouseDown  += new MouseEventHandler(DataGridView_MouseDown);
                DataGridView.MouseMove  += new MouseEventHandler(DataGridView_MouseMove);
                DataGridView.MouseUp    += new MouseEventHandler(DataGridView_MouseUp);
                _dataGridViewEventsSet = true;
            }
        }

        private TrackDataGridViewColumn _Column
        {
            get
            {
                return (TrackDataGridViewColumn)OwningColumn;
            }
        }

        void DataGridView_MouseUp(object sender, MouseEventArgs e)
        {
            _dataGridViewInitialMoveX = 0;
            _isRightSplitterClicked = false;
            this.DataGridView.Cursor = Cursors.Default;
        }

        int _dataGridViewInitialMoveX;
        int _dataGridViewCurrentMoveX;


        void DataGridView_MouseMove(object sender, MouseEventArgs e)
        {
            if (_isRightSplitterClicked)
            {
                object current = _BindingList[_movingFrameIndex];
                int currentWidth = _rect[_movingFrameIndex].Width; //GetValue<int>(current, "Duration");
                Point screenPoint = e.Location; //this.PointToScreen(e.Location);
                if (_dataGridViewCurrentMoveX == screenPoint.X)
                    return;

                _dataGridViewCurrentMoveX = screenPoint.X;

                int delta = _dataGridViewCurrentMoveX - _dataGridViewInitialMoveX;
                int zoomedDelta = delta;//GetScaledWidth(delta) ;

                int preCalculatedWidth = _initialWidth + (zoomedDelta);
                if (preCalculatedWidth == currentWidth || preCalculatedWidth < 0)
                    return;

                SetWidthCore(_movingFrameIndex, preCalculatedWidth);
                RaiseSelectedFrameChanged(current);
                this.DataGridView.InvalidateCell(this);
                assignWidthCount += 1;
                //label1.Text = string.Format("{5}| Width:{0}, Left:{1}, X1:{2}, X2:{3}, Delta:{4}", this.Width, this.Left, screenPoint.X, _initialRightSplitterScreenCoordinatesX, screenPoint.X - _initialRightSplitterScreenCoordinatesX, assignWidthCount);
                string info = string.Format("Init:{0},Delta:{1},ZoomedDelta:{2}",_initialWidth,delta,zoomedDelta) ;
                Debug.WriteLine(info);

            }
        }

        void DataGridView_MouseDown(object sender, MouseEventArgs e)
        {
            _dataGridViewInitialMoveX = e.Location.X;
            _selectedFrameIndex = -1;
            DataGridView.InvalidateColumn(_Column.Index);
        }

		public override Type FormattedValueType
		{
			get
			{
				return typeof(object);
			}
		}


        private void SetValue(object obj, string propertyName, object value)
        {
            PropertyInfo destProperty = obj.GetType().GetProperty(propertyName);
            destProperty.SetValue(obj, value, null);
        }
        private T GetValue<T>(object obj, string propertyName)
        {
            PropertyInfo destProperty = obj.GetType().GetProperty(propertyName);
            return (T)destProperty.GetValue(obj, null);
        }


        private void RecalculateRectangles()
        {
            IList items = _BindingList;//_DataManager.List;  
            if (items == null)
                return;

            if (items.Count != _rect.Count)
                throw new InvalidOperationException(); 

            for (int i = 0; i < items.Count; i++)
            {
                object currentObject = items[i];
                Rectangle currentRect = _rect[i];

                int currentDuration = GetValue<int>(currentObject, "Duration");
                int zoomedDuration =GetScaledWidth(currentDuration ) ;
                _rect[i] = new Rectangle(currentRect.Location, new Size(zoomedDuration, Size.Height)); 

            }
            for (int i = 0; i < _rect.Count-1 ; i++)
            {
                Rectangle previous = _rect[i];
                Rectangle next = _rect[i + 1];

                _rect[i + 1] = new Rectangle(previous.X + previous.Width+1,0  ,next.Width,next.Height)  ;   

            }
        }

        private IBindingList _bindingList;
        private bool _listChangedConnected;



        public IBindingList _BindingList
        {
            get {

                if (Value == null)
                    return null;

                IBindingList bindingList = Value as IBindingList;
                if (bindingList == null)
                    return null;

                if (!_listChangedConnected)
                {
                    ((IBindingList)Value).ListChanged += _dataManager_ListChanged;
                    _listChangedConnected = true;
                }
                return (IBindingList)Value;
            }
        }


        void _dataManager_PositionChanged(object sender, EventArgs e)
        {
        }

        protected override void OnMouseDoubleClick(DataGridViewCellMouseEventArgs e)
        {
            base.OnMouseDoubleClick(e);
            Point clickPoint = e.Location;

            Rectangle? rect = GetRectangleByMouseX(e.Location.X);
            if (rect == null || !rect.HasValue)
                return;

            int frameIndex = GetRectangleIndexByMouseX(e.X ) ;
            object frame = _BindingList[frameIndex] ;

            string propName = string.Empty; 
 
            if (clickPoint.X < rect.Value.Width / 2)
            {
                propName = "StartColor";
            }
            else
            {
                propName = "EndColor";
            }

            bool result = _Column.OnFrameEditing(frame, propName);
            if (result)
            {
                RaiseSelectedFrameChanged(frame);
            }

        }

        void _dataManager_ListChanged(object sender, System.ComponentModel.ListChangedEventArgs e)
        {
            switch (e.ListChangedType)
            {
                case System.ComponentModel.ListChangedType.ItemAdded:
                    _rect.Add(Rectangle.Empty);
                    break;
                case System.ComponentModel.ListChangedType.ItemChanged:
                    break;
                case System.ComponentModel.ListChangedType.ItemDeleted:
                    _rect.RemoveAt(e.NewIndex  );
                    break;
                case System.ComponentModel.ListChangedType.ItemMoved:
                    break;
                case System.ComponentModel.ListChangedType.PropertyDescriptorAdded:
                    break;
                case System.ComponentModel.ListChangedType.PropertyDescriptorChanged:
                    break;
                case System.ComponentModel.ListChangedType.PropertyDescriptorDeleted:
                    break;
                case System.ComponentModel.ListChangedType.Reset:
                    break;
                default:
                    break;
            }
            this.DataGridView.InvalidateCell(this);  
        }

        void _dataManager_CurrentChanged(object sender, EventArgs e)
        {
        }

      

		protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
		{
			base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);

            TextBoxRenderer.DrawTextBox(graphics, cellBounds, TextBoxState.Normal);      

            Rectangle content = GetContentBounds(graphics, cellStyle, rowIndex);   
            if (value == null)
                return;

            RecalculateRectangles();
            for (int i = 0; i < _rect.Count ; i++)
            {
                Rectangle eachRect = _rect[i];
                eachRect.Offset(cellBounds.Location.X  , cellBounds.Location.Y );
                PaintFrameContent(graphics, eachRect, i);
            }
		}

        private int GetScaledWidth(int width)
        {
            return (int)_Column.ScaleFactor * width / 100;
        }
        private int GetInitialWidth(int scaledWidth)
        {
            return (int)(scaledWidth * 100 / _Column.ScaleFactor);
        }

        private void Scale(ref Rectangle eachRect)
        {
            float factor = _Column.ScaleFactor;
            int width = GetScaledWidth(eachRect.Width );
            eachRect = new Rectangle(eachRect.Location, new Size(width, eachRect.Height));
 
        }

        int _selectedFrameIndex = -1;

        private void PaintFrameContent(Graphics graphics,Rectangle rect,int index)
        {
            graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
            graphics.SmoothingMode = SmoothingMode.AntiAlias;

            object value = _BindingList[index];
            Color startColor = GetValue<Color>(value, "StartColor");
            Color endColor = GetValue<Color>(value, "EndColor");


            if (index == _selectedFrameIndex)
            {
                using (Brush brush = new LinearGradientBrush(rect, Color.White, Color.Silver, LinearGradientMode.Vertical))
                {
                    graphics.FillRectangle(brush, rect);
                }
            }
            int customPadding = rect.Height / 5;

            Point topLeft = new Point(rect.Location.X, rect.Location.Y + customPadding);
            Point bottomLeft = new Point(rect.Location.X, rect.Location.Y + rect.Height - customPadding);

            Point topRight = new Point(rect.Location.X + rect.Width, rect.Location.Y + customPadding);
            Point bottomRight = new Point(rect.Location.X + rect.Width, rect.Location.Y + rect.Height - customPadding);

            if (startColor.ToArgb() != endColor.ToArgb())
            {
                using (Brush brushStart = new SolidBrush(startColor))
                {
                    graphics.FillPolygon(brushStart, new Point[] { topLeft, topRight, bottomLeft, topLeft });
                }
                using (Brush brushEnd = new SolidBrush(endColor))
                {
                    graphics.FillPolygon(brushEnd, new Point[] { bottomLeft, topRight, bottomRight, bottomLeft });
                }

                using (Pen black = new Pen(Color.Black, 1))
                {
                    graphics.DrawLine(black, bottomLeft, topRight);
                }
            }
            else
            {
                using (Brush brushStart = new SolidBrush(startColor))
                {
                    graphics.FillRectangle(brushStart, topLeft.X, topLeft.Y, rect.Width, rect.Height - 2 * customPadding);
                }
            }

            using (Pen blackPen = new Pen(Color.Black, 1))
            {
                graphics.DrawLine(blackPen, new Point(rect.Location.X + rect.Width, rect.Location.Y), new Point(rect.Location.X + rect.Width, rect.Location.Y + rect.Height));
                graphics.DrawLine(blackPen, topLeft, topRight);
                graphics.DrawLine(blackPen, bottomLeft, bottomRight);
            }
        }

		private List<Rectangle> _rect;

		private Rectangle? GetLastRectangle()
		{
			if (_rect == null)
				throw new InvalidOperationException();

			if (_rect.Count == 0)
				return null;
			else
				return _rect[_rect.Count - 1];
		}

		private Rectangle? GetRectangleByMouseX(int mouseX)
		{
			if (_rect == null)
				throw new InvalidOperationException();

            //foreach (Rectangle eachRect in _rect)
            //{
            //    if (mouseX >= eachRect.X && mouseX <= eachRect.X + eachRect.Width)
            //        return eachRect;
            //}
            int index = GetRectangleIndexByMouseX(mouseX);
            if (index < 0)
                return null;
            else
                return _rect[index ] ;
		}

        private int GetRectangleIndexByMouseX(int mouseX)
        {
            if (_rect == null)
                throw new InvalidOperationException();
            Rectangle eachRect = Rectangle.Empty;
            for (int i = 0; i < _rect.Count; i++)
            //foreach (Rectangle eachRect in _rect)
            {
                eachRect = _rect[i];
                if (mouseX >= eachRect.X && mouseX <= eachRect.X + eachRect.Width)
                    return i;
            }

            return -1;
        }


		//protected override void OnPaint(PaintEventArgs pe)
		//{
		//    base.OnPaint(pe);
		//    foreach (Rectangle eachRect in _rect)
		//    {

		//        pe.Graphics.FillRectangle(Brushes.WhiteSmoke, eachRect);
		//    }
		//}

        protected override void OnMouseMove(DataGridViewCellMouseEventArgs e)
        {
            base.OnMouseMove(e);
            int index = GetRectangleIndexByMouseX(e.X);
            Rectangle? current = GetRectangleByMouseX(e.X);
            if (current == null || !current.HasValue)
            {
                this.DataGridView.Cursor = Cursors.Default;
                return;
            }
            if (e.X > current.Value.X + current.Value.Width - 10 && e.X < current.Value.X + current.Value.Width)
            {
                this.DataGridView.Cursor = Cursors.SizeWE;
            }
            else
            {
                this.DataGridView.Cursor = Cursors.Default;
            }

            //if (_isRightSplitterClicked)
            //{
            //    Point screenPoint = e.Location; //this.PointToScreen(e.Location);
            //    if (_currentRightSplitterScreenCoordinatesX == screenPoint.X)
            //        return;

            //    _currentRightSplitterScreenCoordinatesX = screenPoint.X;

            //    int preCalculatedWidth = _initialWidth + (_currentRightSplitterScreenCoordinatesX - _initialRightSplitterScreenCoordinatesX);
            //    if (preCalculatedWidth == current.Value.Width || preCalculatedWidth < 0)
            //        return;
            //    //Rectangle newRect= new Rectangle(current.Value.Location, new Size(preCalculatedWidth,current.Value.Height ));
            //    SetWidthCore(index, preCalculatedWidth);
            //    this.DataGridView.InvalidateCell(this);
            //    //base.DataGridView.InvalidateCell(e.ColumnIndex, e.RowIndex);
            //    assignWidthCount += 1;
            //    //label1.Text = string.Format("{5}| Width:{0}, Left:{1}, X1:{2}, X2:{3}, Delta:{4}", this.Width, this.Left, screenPoint.X, _initialRightSplitterScreenCoordinatesX, screenPoint.X - _initialRightSplitterScreenCoordinatesX, assignWidthCount);
            //}

        }


        private void SetWidthCore(int index, int width)
        {
            //_rect[index] = newRectangle;
            object frame = _BindingList[index];
            if (frame == null)
                throw new InvalidOperationException();

            int zoomedDelta = GetInitialWidth (width);
            SetValue(frame, "Duration", zoomedDelta);
            RecalculateRectangles();


        }
        bool _isRightSplitterClicked;
        int _initialRightSplitterScreenCoordinatesX;
        int _initialWidth;
        int assignWidthCount = 0;

        int _currentRightSplitterScreenCoordinatesX;

        int _movingFrameIndex;

        private void RaiseSelectedFrameChanged(object frame)
        {
            _Column.OnSelectedFrameChanged(frame);
        }
        protected override void OnMouseDown(DataGridViewCellMouseEventArgs e)
        {
            base.OnMouseDown(e);
            _movingFrameIndex = GetRectangleIndexByMouseX(e.X);
            _selectedFrameIndex = _movingFrameIndex;
           // if (currentMovingIndex != _movingFrameIndex)
           // {
                object frame = null;
                if (_movingFrameIndex>-1)
                {
                    frame = _BindingList[_movingFrameIndex ] ;
                }
                RaiseSelectedFrameChanged(frame);
           // }
             


            Rectangle? current = GetRectangleByMouseX(e.X);

            if (current == null || !current.HasValue)
            {
                this.DataGridView.Cursor = Cursors.Default;
                return;
            }

            if (e.Location.X > current.Value.X + current.Value.Width - 10 && e.Location.X < current.Value.X + current.Value.Width)
            {
                _isRightSplitterClicked = true;
                //Cursor = Cursors.VSplit; 
                Point screenPoint = e.Location ;
                _initialRightSplitterScreenCoordinatesX = screenPoint.X;
                _initialWidth = current.Value.Width;
                assignWidthCount = 0;
            }

        }

        protected override void OnMouseUp(DataGridViewCellMouseEventArgs e)
        {
            base.OnMouseUp(e);
            _isRightSplitterClicked = false;
            //Cursor = Cursors.Default; 

        }

		private void Add(int width)
		{
			Rectangle? last = GetLastRectangle();
			Rectangle newRect = Rectangle.Empty;
			if (last == null || !last.HasValue)
			{
				newRect = new Rectangle(Point.Empty, new Size(width, this.Size.Height));
			}
			else
			{
				newRect = new Rectangle(new Point(last.Value.X + last.Value.Width + 1, 0), new Size(width, this.Size.Height));
			}

			_rect.Add(newRect);
			//Invalidate();
		}
    }

    internal class FrameInternal
    {
        public Point Location { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }

        public float Duration { get; set; }
    }

}
